package com.cino.factory;

import com.cino.config.properties.LogStorageProperties;
import com.cino.enums.StoreType;
import com.cino.strategy.AuditLogStorageStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 审计日志存储策略工厂类
 * 
 * 职责：
 * 1. 根据注解配置和yml配置选择合适的存储策略
 * 2. 实现注解配置优先级高于yml配置的逻辑
 * 3. 提供策略缓存，提高性能
 * 4. 支持策略可用性检查
 * 

 * 优先级规则：
 * 1. 注解中的storeType配置优先级最高
 * 2. application.yml中的配置为默认配置
 * 3. 如果都没有配置，默认使用mysql
 */
@Slf4j
@Component
public class AuditLogStorageStrategyFactory {
    
    @Autowired
    private LogStorageProperties logStorageProperties;
    
    @Autowired
    private List<AuditLogStorageStrategy> strategies;
    
    /**
     * 策略缓存，提高性能
     * key: 策略类型（mysql/local）
     * value: 策略实例
     */
    private final Map<String, AuditLogStorageStrategy> strategyCache = new ConcurrentHashMap<>();
    
    /**
     * 工厂初始化状态
     */
    private boolean initialized = false;
    
    /**
     * 工厂初始化方法
     * 在Spring容器完成依赖注入后自动调用
     */
    @PostConstruct
    public void initializeFactory() {
        log.info(">>> [策略工厂] 初始化审计日志存储策略工厂...");
        
        // 预热策略缓存
        if (strategies != null && !strategies.isEmpty()) {
            for (AuditLogStorageStrategy strategy : strategies) {
                strategyCache.put(strategy.getStrategyType(), strategy);
                log.debug(">>> [策略工厂] 注册策略: {} -> {}",
                        strategy.getStrategyType(), strategy.getClass().getSimpleName());
            }
        }
        
        initialized = true;
        log.info(">>> [策略工厂] 初始化完成，可用策略: {}", strategyCache.keySet());
    }
    
    /**
     * 检查工厂是否已初始化
     */
    private void ensureInitialized() {
        if (!initialized) {
            //TODO替换BusinessException
            throw new IllegalStateException("策略工厂尚未初始化，请检查Spring配置");
        }
    }
    
    /**
     * 根据注解配置获取存储策略
     * 
     * @param annotationStoreTypes 注解中配置的存储类型数组（通常只包含一个元素）
     * @return 选中的存储策略
     */
    public AuditLogStorageStrategy getStrategy(StoreType[] annotationStoreTypes) {
        ensureInitialized();
        
        // 1. 优先处理注解配置的存储策略
        if (annotationStoreTypes != null && annotationStoreTypes.length > 0) {
            StoreType storeType = annotationStoreTypes[0]; // 注解中只配置一个存储类型
            AuditLogStorageStrategy strategy = getStrategyByType(storeType.getCode());
            if (strategy != null && strategy.isAvailable()) {
                log.debug(">>> [策略工厂] 使用注解配置的存储策略: {}", storeType.getCode());
                return strategy;
            }
            log.warn(">>> [策略工厂] 注解配置的存储策略不可用: {}, 降级使用yml配置", storeType.getCode());
        }
        
        // 2. 使用yml配置的active字段
        String activeType = getActiveTypeFromConfig();
        if (activeType != null && !activeType.trim().isEmpty()) {
            AuditLogStorageStrategy strategy = getStrategyByType(activeType);
            if (strategy != null && strategy.isAvailable()) {
                log.debug(">>> [策略工厂] 使用yml配置的active存储策略: {}", activeType);
                return strategy;
            }
            log.warn(">>> [策略工厂] yml配置的active存储策略不可用: {}, 降级使用已加载的策略", activeType);
        }
        
        // 3. 从已加载的策略中选择第一个可用的
        // 注意：Spring容器已经通过@ConditionalOnListProperty控制了策略的加载
        // MySQL策略设置了matchIfMissing=true，所以总是会被加载作为保底策略
        for (AuditLogStorageStrategy strategy : strategies) {
            if (strategy.isAvailable()) {
                log.debug(">>> [策略工厂] 使用已加载的存储策略: {}", strategy.getStrategyType());
                return strategy;
            }
        }
        
        // 4. 没有可用策略（理论上不应该发生，因为MySQL策略设置了matchIfMissing=true）
        //TODO替换BusinessException
        throw new IllegalStateException("没有可用的审计日志存储策略");
    }
    
    /**
     * 根据存储类型获取策略实例
     * 
     * @param strategyType 策略类型（mysql/local）
     * @return 策略实例，如果不存在返回null
     */
    private AuditLogStorageStrategy getStrategyByType(String strategyType) {
        if (strategyType == null || strategyType.trim().isEmpty()) {
            return null;
        }
        
        // 先从缓存获取
        AuditLogStorageStrategy cachedStrategy = strategyCache.get(strategyType);
        if (cachedStrategy != null) {
            return cachedStrategy;
        }
        
        // 从策略列表中查找
        for (AuditLogStorageStrategy strategy : strategies) {
            if (strategyType.equalsIgnoreCase(strategy.getStrategyType())) {
                // 加入缓存
                strategyCache.put(strategyType, strategy);
                log.debug(">>> [策略工厂] 找到策略实例: {}", strategyType);
                return strategy;
            }
        }
        
        log.warn(">>> [策略工厂] 未找到策略实例: {}", strategyType);
        return null;
    }
    
    /**
     * 从配置文件获取激活的存储类型
     * 
     * @return 激活的存储类型
     */
    private String getActiveTypeFromConfig() {
        if (logStorageProperties != null && logStorageProperties.getActive() != null) {
            String active = logStorageProperties.getActive();
            log.debug(">>> [策略工厂] yml配置激活的存储类型: {}", active);
            return active;
        }
        
        log.debug(">>> [策略工厂] yml配置中未找到激活的存储类型");
        return null;
    }
    
    
    /**
     * 获取所有可用的策略信息（用于调试和监控）
     * 
     * @return 策略可用性信息
     */
    public String getAvailableStrategiesInfo() {
        StringBuilder info = new StringBuilder();
        info.append("可用策略列表：\n");
        
        for (AuditLogStorageStrategy strategy : strategies) {
            info.append("- ").append(strategy.getStrategyType())
                .append(": ").append(strategy.isAvailable() ? "可用" : "不可用")
                .append("\n");
        }
        
        return info.toString();
    }
    
    /**
     * 清除策略缓存（用于配置更新时重新加载）
     */
    public void clearCache() {
        strategyCache.clear();
        log.info(">>> [策略工厂] 策略缓存已清除");
    }
    
    /**
     * 检查指定策略是否可用
     * 
     * @param strategyType 策略类型
     * @return true-可用，false-不可用
     */
    public boolean isStrategyAvailable(String strategyType) {
        AuditLogStorageStrategy strategy = getStrategyByType(strategyType);
        return strategy != null && strategy.isAvailable();
    }
    
}
